﻿using System;
using System.Diagnostics.Contracts;
using System.Linq;

namespace MathService
{
    public partial struct Complex
    {
        public static Complex operator -(Complex z)
        {
            return new Complex(-z.Re, -z.Im);
        }
        public static Complex operator +(Complex z)
        {
            return new Complex(z.Re, z.Im);
        }

        public static Complex operator !(Complex z)
        {
            return z.ComplexConjugate;
        }

        /// <summary>Оператор сложения</summary>
        /// <param name="X">Первое слогаемое</param>
        /// <param name="Y">Второе слогаемое</param>
        /// <returns>Сумма комплексных чисел</returns>
        public static Complex operator +(Complex X, Complex Y)
        {
            return new Complex(X.Re + Y.Re, X.Im + Y.Im);
        }

        /// <summary>Оператор разности комплексных чисел</summary>
        /// <param name="X">Уменьшаемое комплексное число</param>
        /// <param name="Y">Вычитаемое комплексное число</param>
        /// <returns>Разность комплексных чисел</returns>
        public static Complex operator -(Complex X, Complex Y)
        {
            return new Complex(X.Re - Y.Re, X.Im - Y.Im);
        }

        /// <summary>Оператор сложения</summary>
        /// <param name="X">Первое слогаемое</param>
        /// <param name="Y">Второе слогаемое</param>
        /// <returns>Сумма комплексных чисел</returns>
        public static Complex operator +(double X, Complex Y)
        {
            return new Complex(X + Y.Re, Y.Im);
        }
        public static Complex operator +(int X, Complex Y)
        {
            return new Complex(X + Y.Re, Y.Im);
        }
        public static Complex operator +(float X, Complex Y)
        {
            return new Complex(X + Y.Re, Y.Im);
        }

        /// <summary>Оператор разности комплексных чисел</summary>
        /// <param name="X">Уменьшаемое комплексное число</param>
        /// <param name="Y">Вычитаемое комплексное число</param>
        /// <returns>Разность комплексных чисел</returns>
        public static Complex operator -(double X, Complex Y)
        {
            return new Complex(X - Y.Re, Y.Im);
        }
        public static Complex operator -(int X, Complex Y)
        {
            return new Complex(X - Y.Re, Y.Im);
        }
        public static Complex operator -(float X, Complex Y)
        {
            return new Complex(X - Y.Re, Y.Im);
        }

        /// <summary>Оператор сложения</summary>
        /// <param name="X">Первое слогаемое</param>
        /// <param name="Y">Второе слогаемое</param>
        /// <returns>Сумма комплексных чисел</returns>
        public static Complex operator +(Complex X, double Y)
        {
            return new Complex(X.Re + Y, X.Im);
        }
        public static Complex operator +(Complex X, int Y)
        {
            return new Complex(X.Re + Y, X.Im);
        }
        public static Complex operator +(Complex X, float Y)
        {
            return new Complex(X.Re + Y, X.Im);
        }

        /// <summary>Оператор разности комплексных чисел</summary>
        /// <param name="X">Уменьшаемое комплексное число</param>
        /// <param name="Y">Вычитаемое комплексное число</param>
        /// <returns>Разность комплексных чисел</returns>
        public static Complex operator -(Complex X, double Y)
        {
            return new Complex(X.Re - Y, X.Im);
        }
        public static Complex operator -(Complex X, int Y)
        {
            return new Complex(X.Re - Y, X.Im);
        }
        public static Complex operator -(Complex X, float Y)
        {
            return new Complex(X.Re - Y, X.Im);
        }

        /// <summary>Оператор умножения комплексного числа на вещественное</summary>
        /// <param name="X">Комплексное число</param>
        /// <param name="Y">Вещественное число</param>
        /// <returns>Комплексное произведение</returns>
        public static Complex operator *(Complex X, double Y)
        {
            return new Complex(X.Re * Y, X.Im * Y);
        }
        public static Complex operator *(Complex X, int Y)
        {
            return new Complex(X.Re * Y, X.Im * Y);
        }
        public static Complex operator *(Complex X, float Y)
        {
            return new Complex(X.Re * Y, X.Im * Y);
        }

        /// <summary>Оператор деления комплексного числа на вещественное</summary>
        /// <param name="X">Комплексное делимое число</param>
        /// <param name="Y">Вещественный делитель</param>
        /// <returns>Комплексное частное</returns>
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex operator /(Complex X, double Y)
        {
            Contract.Requires(Y != 0);
            return new Complex(X.Re / Y, X.Im / Y);
        }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex operator /(Complex X, int Y)
        {
            Contract.Requires(Y != 0);
            return new Complex(X.Re / Y, X.Im / Y);
        }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex operator /(Complex X, float Y)
        {
            Contract.Requires(Y != 0);
            return new Complex(X.Re / Y, X.Im / Y);
        }

        /// <summary>Оператор произведения вещественного и комплексного числа</summary>
        /// <param name="X">Вещественное число</param>
        /// <param name="Y">Комплексное число</param>
        /// <returns>Комплексное произведение</returns>
        public static Complex operator *(double X, Complex Y)
        {
            return new Complex(Y.Re * X, Y.Im * X);
        }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex operator *(int X, Complex Y)
        {
            return new Complex(Y.Re * X, Y.Im * X);
        }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex operator *(float X, Complex Y)
        {
            return new Complex(Y.Re * X, Y.Im * X);
        }

        /// <summary>Оператор деления вещественного и комплексного числа</summary>
        /// <param name="X">Вещественное делимое число</param>
        /// <param name="Y">Комплексный делитель</param>
        /// <returns>Комплексное частное</returns>
        public static Complex operator /(double X, Complex Y)
        {
            var re = Y.Re;
            var im = Y.Im;

            var q = 1 / (re * re + im * im);
            re = X * re * q;
            im = -X * im * q;
            return new Complex(re, im);
        }
        public static Complex operator /(int X, Complex Y)
        {
            var re = Y.Re;
            var im = Y.Im;
            var q = 1 / (re * re + im * im);
            return new Complex(X * re * q, -X * im * q);
        }
        public static Complex operator /(float X, Complex Y)
        {
            var re = Y.Re;
            var im = Y.Im;
            var q = 1 / (re * re + im * im);
            return new Complex(X * re * q, -X * im * q);
        }

        /// <summary>Оператор произведения двух комплексных чисел</summary>
        /// <param name="X">Первый множитель</param>
        /// <param name="Y">Второй множитель</param>
        /// <returns>Комплексное произведение</returns>
        public static Complex operator *(Complex X, Complex Y)
        {
            return new Complex(X.Re * Y.Re - X.Im * Y.Im, X.Re * Y.Im + X.Im * Y.Re);
        }

        /// <summary>Оператор деления двух комплексных чисел</summary>
        /// <param name="X">Делимое комплексное число</param>
        /// <param name="Y">Делитель комплексного числа</param>
        /// <returns>Частное двух комплексных чисел</returns>
        public static Complex operator /(Complex X, Complex Y)
        {
            var re_x = X.Re;
            var im_x = X.Im;
            var re_y = Y.Re;
            var im_y = Y.Im;
            var q = 1 / (re_y * re_y + im_y * im_y);
            return new Complex((re_x * re_y + im_x * im_y) * q, (im_x * re_y - re_x * im_y) * q);
        }

        /// <summary>Возведение комплексного числа в вещественную степень по формуле Муавра</summary>
        /// <param name="Z">Возводимое в степень комплексное число Z^X</param>
        /// <param name="X">Вещественный показатель степени Z^X</param>
        /// <returns>Z^X</returns>       
        public static Complex operator ^(Complex Z, double X)
        {
            return Exp(Math.Pow(Z.Abs, X), Z.Arg * X);
        }
        public static Complex operator ^(Complex Z, float X)
        {
            return Exp(Math.Pow(Z.Abs, X), Z.Arg * X);
        }
        public static Complex operator ^(Complex Z, int X)
        {
            return Exp(Math.Pow(Z.Abs, X), Z.Arg * X);
        }

        /// <summary>Оператор возведения вещественного числа в комплексную степень</summary>
        /// <param name="X">Вещественное число</param>
        /// <param name="Z">Комплексная степень</param>
        /// <returns>Комплексный результат возведения действистельного числа в комплексную степень</returns>
        public static Complex operator ^(double X, Complex Z)
        {
            return Exp(Math.Pow(X, Z.Re), Math.Log(X) * Z.Im);
        }
        public static Complex operator ^(int X, Complex Z)
        {
            return Exp(Math.Pow(X, Z.Re), Math.Log(X) * Z.Im);
        }
        public static Complex operator ^(float X, Complex Z)
        {
            return Exp(Math.Pow(X, Z.Re), Math.Log(X) * Z.Im);
        }

        /// <summary>Оператор возведения комплексного числа в комплексную степень</summary>
        /// <param name="X">Комплексное основание экспоненты</param>
        /// <param name="Y">Комплексный показатель степени</param>
        /// <returns>Комплексный результат возведения комплексного числа в комплексную степень</returns>
        public static Complex operator ^(Complex X, Complex Y)
        {
            var r = X.Abs;
            var arg = X.Arg;

            var R = Math.Pow(r, Y.Re) * Math.Pow(Math.E, -arg * Y.Im);
            var Arg = arg * Y.Re + Math.Log(r) * Y.Im;

            return Exp(R, Arg);
        }

        /* -------------------------------------------------------------------------------------------- */

        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator +(int[] X, Complex Y) { return X.Select(x => x + Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator +(float[] X, Complex Y) { return X.Select(x => x + Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator +(double[] X, Complex Y) { return X.Select(x => x + Y).ToArray(); }

        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator -(int[] X, Complex Y) { return X.Select(x => x - Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator -(float[] X, Complex Y) { return X.Select(x => x - Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator -(double[] X, Complex Y) { return X.Select(x => x - Y).ToArray(); }

        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator *(int[] X, Complex Y) { return X.Select(x => x * Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator *(float[] X, Complex Y) { return X.Select(x => x * Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator *(double[] X, Complex Y) { return X.Select(x => x * Y).ToArray(); }

        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator /(int[] X, Complex Y) { return X.Select(x => x / Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator /(float[] X, Complex Y) { return X.Select(x => x / Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator /(double[] X, Complex Y) { return X.Select(x => x / Y).ToArray(); }

        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator +(Complex[] X, Complex Y) { return X.Select(x => x + Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator -(Complex[] X, Complex Y) { return X.Select(x => x - Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator *(Complex[] X, Complex Y) { return X.Select(x => x * Y).ToArray(); }
        [System.Diagnostics.DebuggerStepThrough]
        public static Complex[] operator /(Complex[] X, Complex Y) { return X.Select(x => x / Y).ToArray(); }

        /* -------------------------------------------------------------------------------------------- */

        /// <summary>Оператор сравнения двух комплексных чисел (равенства)</summary>
        /// <returns>Истина, если числа равны между собой</returns>
        [System.Diagnostics.DebuggerStepThrough]
        public static bool operator ==(Complex X, Complex Y)
        {
            return Math.Abs(X.f_Re - Y.f_Re) < double.Epsilon && Math.Abs(X.f_Im - Y.f_Im) < double.Epsilon;
        }

        /// <summary>Оператор сравнения двух комплексных чисел (неравенства)</summary>
        /// <returns>Истина, если числа не равны</returns>
        [System.Diagnostics.DebuggerStepThrough]
        public static bool operator !=(Complex X, Complex Y)
        {
            return Math.Abs(X.f_Re - Y.f_Re) > double.Epsilon && Math.Abs(X.f_Im - Y.f_Im) > double.Epsilon;
        }

        /* -------------------------------------------------------------------------------------------- */

        /// <summary>ОПератор неявного приведения к дробному типу чисел с двойной точностью</summary>
        /// <param name="Z">Приводимое комплексное число</param>
        /// <returns>Модуль комплексного числа</returns>
        [System.Diagnostics.DebuggerStepThrough]
        public static explicit operator double(Complex Z) { return Z.Abs; }

        /// <summary>Оператор неявного приведения дробного числа двойной точности к комплексному виду</summary>
        /// <param name="X">Вещественное число двойной точности</param>
        /// <returns>Комплексное число</returns>
        [System.Diagnostics.DebuggerStepThrough]
        public static implicit operator Complex(double X)
        {
            Contract.Requires(!double.IsNaN(X));
            return new Complex(X);
        }

        /* -------------------------------------------------------------------------------------------- */

    }
}
